package com.mingserve.raw.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mingserve.common.core.constant.RawConstants;
import com.mingserve.common.core.utils.*;
import com.mingserve.common.mqtt.utils.MqttUtils;
import com.mingserve.common.mqtt.utils.NetUtils;
import com.mingserve.raw.domain.RawConn;
import com.mingserve.raw.domain.RawGroup;
import com.mingserve.raw.domain.RawTime;
import com.mingserve.raw.domain.dto.RawConnDto;
import com.mingserve.raw.domain.dto.RawDataDto;
import com.mingserve.raw.domain.dto.SelectDto;
import com.mingserve.raw.domain.vo.FeatureVo;
import com.mingserve.raw.domain.vo.RawConnVo;
import com.mingserve.raw.domain.vo.RawTempVo;
import com.mingserve.raw.domain.vo.RawVo;
import com.mingserve.raw.mapper.RawConnMapper;
import com.mingserve.raw.mapper.RawGroupMapper;
import com.mingserve.raw.mapper.RawTimeMapper;
import com.mingserve.raw.service.IRawService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 索引记录Service业务层处理
 *
 * @author lvwshuai
 * @date 2024-02-01
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class RawServiceImpl implements IRawService
{
    private final RawGroupMapper rawGroupMapper;
    private final RawTimeMapper rawTimeMapper;
    private final RawConnMapper rawConnMapper;
    private final MqttUtils mqttUtils;

    /**
     * 新增攒包索引
     *
     * @param rawGroup 攒包索引对象
     * @return 结果
     */
    @Override
    public Boolean insertRawGroup(RawGroup rawGroup) {
        return rawGroupMapper.insertOrUpdate(rawGroup);
    }

    /**
     * 新增测点索引
     *
     * @param rawTime
     * @return
     */
    @Override
    public Boolean insertRawTime(RawTime rawTime) {
        LambdaQueryWrapper<RawTime> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RawTime::getTs, rawTime.getTs());
        lqw.eq(RawTime::getPointCode, rawTime.getPointCode());
        RawTime existingRecord = rawTimeMapper.selectOne(lqw);
        return existingRecord == null && rawTimeMapper.insertOrUpdate(rawTime);
    }

    /**
     * 新增测点统计包索引
     *
     * @param rawConn
     * @return
     */
    @Override
    public Boolean insertRawConn(RawConn rawConn) {
        LambdaQueryWrapper<RawConn> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RawConn::getTs, rawConn.getTs());
        lqw.eq(RawConn::getPointCode, rawConn.getPointCode());
        RawConn existingRecord = rawConnMapper.selectOne(lqw);
        return existingRecord == null && rawConnMapper.insertOrUpdate(rawConn);
    }

    /**
     * 测点最后一次上传的日期
     *
     * @param pointCode
     * @return
     */
    @Override
    public Long getLastDate(String pointCode) {
        LambdaQueryWrapper<RawTime> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RawTime::getPointCode, pointCode);
        lqw.orderByDesc(RawTime::getTs);
        lqw.last("limit 1");
        RawTime rawTime = rawTimeMapper.selectOne(lqw,false);
        return rawTime == null ? null : rawTime.getTs();
    }

    /**
     * 查询测点某日期下的时间列表
     *
     * @param selectDto
     * @return
     */
    @Override
    public List<Long> getTimeList(SelectDto selectDto) {
        List<RawTime> list = queryRawTime(selectDto);
        return list == null ? null : list.stream().map(RawTime::getTs).collect(Collectors.toList());
    }

    /**
     * 查询测点某时间段数据
     *
     * @param selectDto
     * @return
     */
    @Override
    public List<RawTime> queryRawTime(SelectDto selectDto)
    {
        LambdaQueryWrapper<RawTime> lqw = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(selectDto.getPointCode())) {
            if (selectDto.getPointCode().contains(",")) {
                List<String> pointCodes = Arrays.asList(selectDto.getPointCode().split(","));
                lqw.in(RawTime::getPointCode, pointCodes);
            } else {
                lqw.eq(RawTime::getPointCode, selectDto.getPointCode());
            }
        }
        lqw.between(RawTime::getTs, selectDto.getBeginTime(), selectDto.getEndTime());
        lqw.orderByAsc(RawTime::getTs);
        return rawTimeMapper.selectList(lqw);
    }

    /**
     * 查询测点某时间段原始数据
     *
     * @param selectDto
     * @return
     */
    List<RawDataDto> queryRawData(SelectDto selectDto)
    {
        List<RawTime> rawTimeList = queryRawTime(selectDto);
        List<RawDataDto> rawDataDtoList = new ArrayList<>();
        for (RawTime rawTime : rawTimeList) {
            String json = getRawJson(rawTime, "acc");
            if (json == null) {
                rawTimeMapper.deleteById(rawTime);
                continue;
            }
            try {
                RawDataDto rawDataDto = JSON.parseObject(json, RawDataDto.class);
                rawDataDtoList.add(rawDataDto);
            } catch (Exception e) {
                log.error("Raw data serialization failed", e);
            }
        }
        return rawDataDtoList;
    }

    /**
     * 查询测点某时间戳下的波形
     *
     * @param selectDto
     * @return
     */
    @Override
    public RawVo getVibRaw(SelectDto selectDto)
    {
        RawDataDto rawDataDto = getRawData(selectDto);
        if (rawDataDto == null) return null;
        RawVo rawVo = new RawVo();
        rawVo.setTimestamp(rawDataDto.getTimestamp());
        rawVo.setT(SignalUtils.intervals(rawDataDto.getLength(), rawDataDto.getVibSampleFrequency()));
        if ("acc".equals(selectDto.getPhysicalType())) {
            RawDataDto.processRawAcc(rawDataDto);
            rawVo.setValues(rawDataDto.getAccX(), rawDataDto.getAccY(), rawDataDto.getAccZ(), selectDto.getAxial());
        } else if ("vel".equals(selectDto.getPhysicalType())) {
            rawVo.setValues(rawDataDto.getVelX(), rawDataDto.getVelY(), rawDataDto.getVelZ(), selectDto.getAxial());
        }
        boolean isEnv = "env".equals(selectDto.getSignalType());
        if (isEnv) {
            rawVo.setValues(SignalUtils.env(rawVo.getX()), SignalUtils.env(rawVo.getY()), SignalUtils.env(rawVo.getZ()), selectDto.getAxial());
        }
        return rawVo;
    }

    /**
     * 查询测点某时间戳下的波形FFT
     *
     * @param selectDto
     * @return
     */
    @Override
    public RawVo getVibFFT(SelectDto selectDto)
    {
        RawDataDto rawDataDto = getRawData(selectDto);
        if (rawDataDto == null) return null;
        RawVo rawVo = new RawVo();
        rawVo.setTimestamp(rawDataDto.getTimestamp());
        int freq = rawDataDto.getVibSampleFrequency();
        boolean isAcc = "acc".equals(selectDto.getPhysicalType());

        for (char axial : selectDto.getAxial().toCharArray()) {
            double[] sourceData;
            switch (axial) {
                case 'x':
                    sourceData = isAcc ? rawDataDto.getAccX() : rawDataDto.getVelX();
                    break;
                case 'y':
                    sourceData = isAcc ? rawDataDto.getAccY() : rawDataDto.getVelY();
                    break;
                case 'z':
                    sourceData = isAcc ? rawDataDto.getAccZ() : rawDataDto.getVelZ();
                    break;
                default:
                    continue; // 跳过无效轴向
            }

            double[][] data = SignalUtils.fft(sourceData, freq);
            if (data != null) {
                rawVo.setValues(data[0], String.valueOf(axial));
                rawVo.setT(data[1]);
            }
        }
        return rawVo;
    }

    @Value("${server.api.url:''}")
    private String serverUrl;

    /**
     * 读取json文件
     *
     * @param selectDto
     * @return
     */
    RawDataDto getRawData(SelectDto selectDto)
    {
        // 2025-05-23 vel 波形调用接口
        if ("vel".equals(selectDto.getPhysicalType())) {
            String velUrl = serverUrl + "/api/freq/queryVel?pointCode=" + selectDto.getPointCode() + "&eventTime=" + selectDto.getEventTime() + "&axial=" + selectDto.getAxial();
            try {
                String res = HttpUtils.sendGet(velUrl);
                JSONObject resJson = JSON.parseObject(res);
                if (resJson.containsKey("data")) {
                    return JSON.parseObject(resJson.getString("data"), RawDataDto.class);
                }
            } catch (Exception e) {
                log.error("Query raw vel failed ", e);
            }
        }

        LambdaQueryWrapper<RawTime> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RawTime::getPointCode, selectDto.getPointCode());
        lqw.eq(RawTime::getTs, selectDto.getEventTime());
        lqw.last("limit 1");
        RawTime rawTime = rawTimeMapper.selectOne(lqw, false);
        String json = getRawJson(rawTime, selectDto.getPhysicalType());
        if (json == null) return null;
        try {
            return JSON.parseObject(json, RawDataDto.class);
        } catch (Exception e) {
            log.error("原始数据序列化失败", e);
            return null;
        }
    }

    /**
     * 读取原始Json文件
     *
     * @param rawTime
     * @param dataType  acc vel
     * @return
     */
    @Override
    public String getRawJson(RawTime rawTime, String dataType)
    {
        if (rawTime == null) return null;
        String filePath = "raw/" + DateUtils.datePath(rawTime.getTs()) + "/" + dataType + "/" + rawTime.getDeviceCode() + "/" + rawTime.getTs() + ".json";
        return readFile(filePath);
    }

    /**
     * 读取原始统计包数据
     *
     * @param rawConn
     * @return
     */
    @Override
    public String getRawConn(RawConn rawConn)
    {
        String filePath = "raw/" + DateUtils.datePath(rawConn.getTs()) + "/conn/" + rawConn.getDeviceCode() + "/" + rawConn.getTs() + ".json";
        return readFile(filePath);
    }

    /**
     * 查询测点原始温度
     *
     * @param selectDto
     * @return
     */
    @Override
    public RawTempVo queryRawTemp(SelectDto selectDto)
    {
        List<RawDataDto> rawDataDtoList = queryRawData(selectDto);
        rawDataDtoList = rawDataDtoList.stream().filter(l -> l.getObjectTemperature() != null && l.getObjectTemperature() >= -55 && l.getObjectTemperature() <= 125).sorted(Comparator.comparing(RawDataDto::getTimestamp)).toList();
        RawTempVo rawTempVo = new RawTempVo();
        rawTempVo.setEventTime(rawDataDtoList.stream().map(l -> DateUtils.formatDate(l.getTimestamp())).toArray(String[]::new));
        rawTempVo.setTemperature(rawDataDtoList.stream().map(l -> MathUtils.round(l.getObjectTemperature(), 3)).toArray(Double[]::new));
        return rawTempVo;
    }

    /**
     * 查询测点统计包数据
     *
     * @param selectDto
     * @return
     */
    @Override
    public RawConnVo queryConn(SelectDto selectDto)
    {
        List<RawConn> list = queryRawConn(selectDto);
        List<RawConnDto> rawConnDtoList = new ArrayList<>();
        for (RawConn rawConn : list) {
            String json = getRawConn(rawConn);
            if (json == null) {
                rawConnMapper.deleteById(rawConn);
                continue;
            }
            try {
                RawConnDto rawConnDto = JSON.parseObject(json, RawConnDto.class);
                rawConnDtoList.add(rawConnDto);
            } catch (Exception e) {
                log.error("Raw conn serialization failed", e);
            }
        }
        RawConnVo rawConnVo = new RawConnVo();
        rawConnVo.setEventTime(rawConnDtoList.stream().map(l-> DateUtils.formatDate(l.getTimestamp())).toArray(String[]::new));
        rawConnVo.setWifiRssi(rawConnDtoList.stream().map(RawConnDto::getWifiRssi).toArray(Integer[]::new));
        rawConnVo.setBatteryVoltage(rawConnDtoList.stream().map(l-> MathUtils.round(l.getBatteryVoltage(), 2)).toArray(Double[]::new));
        rawConnVo.setUploadSpeed(rawConnDtoList.stream().map(l-> MathUtils.round(l.getUploadSpeed(), 2)).toArray(Double[]::new));
        return rawConnVo;
    }

    /**
     * 查询测点某时间段统计数据
     *
     * @param selectDto
     * @return
     */
    @Override
    public List<RawConn> queryRawConn(SelectDto selectDto)
    {
        LambdaQueryWrapper<RawConn> lqw = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(selectDto.getPointCode())) {
            if (selectDto.getPointCode().contains(",")) {
                List<String> pointCodes = Arrays.asList(selectDto.getPointCode().split(","));
                lqw.in(RawConn::getPointCode, pointCodes);
            } else {
                lqw.eq(RawConn::getPointCode, selectDto.getPointCode());
            }
        }
        lqw.between(RawConn::getTs, selectDto.getBeginTime(), selectDto.getEndTime());
        lqw.orderByAsc(RawConn::getTs);
        return rawConnMapper.selectList(lqw);
    }

    /**
     * 上传原始数据
     *
     * @param rawTime
     * @return
     */
    @Override
    public boolean uploadRaw(RawTime rawTime)
    {
        String json = getRawJson(rawTime, "acc");
        if (json == null) return false;
        JSONObject obj = JSON.parseObject(json);
        obj.put("pointCode", rawTime.getPointCode());
        log.info("RawAcc upload to the server {}-{}", rawTime.getPointCode(), rawTime.getTs());
        mqttUtils.publish(RawConstants.MQTT_MSW, "edge/raw/" + NetUtils.getMacAddress(), JSON.toJSONString(obj));
        return true;
    }

    /**
     * 删除原始数据文件及索引
     *
     * @param rawTime
     * @return
     */
    @Override
    public int deleteRaw(RawTime rawTime)
    {
        LambdaQueryWrapper<RawTime> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RawTime::getPointCode, rawTime.getPointCode());
        lqw.eq(RawTime::getTs, rawTime.getTs());
        int rows = rawTimeMapper.delete(lqw);
        if (rows > 0) {
            String filePath = "raw/" + DateUtils.datePath(rawTime.getTs()) + "/acc/" + rawTime.getDeviceCode() + "/" + rawTime.getTs() + ".json";
            deleteFile(filePath);
        }
        return rows;
    }

    /**
     * 上传原始统计包
     *
     * @param rawConn
     * @return
     */
    @Override
    public boolean uploadRawConn(RawConn rawConn)
    {
        String json = getRawConn(rawConn);
        if (json == null) return false;
        log.info("RawConn upload to the server {}-{}", rawConn.getPointCode(), rawConn.getTs());
        mqttUtils.publish(RawConstants.MQTT_MSW, "edge/raw/" + NetUtils.getMacAddress(), json);
        return true;
    }

    /**
     * 删除原始统计包及索引
     *
     * @param rawConn
     * @return
     */
    @Override
    public int deleteRawConn(RawConn rawConn)
    {
        LambdaQueryWrapper<RawConn> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RawConn::getPointCode, rawConn.getPointCode());
        lqw.eq(RawConn::getTs, rawConn.getTs());
        int rows = rawConnMapper.delete(lqw);
        if (rows > 0) {
            String filePath = "raw/" + DateUtils.datePath(rawConn.getTs()) + "/conn/" + rawConn.getDeviceCode() + "/" + rawConn.getTs() + ".json";
            deleteFile(filePath);
        }
        return rows;
    }

    /**
     * 特征类型与索引映射
     * 不可变集合Map
     */
    private static final Map<String, Integer> featureIndexMap = Map.ofEntries(
            Map.entry("avgAbs", 0),
            Map.entry("peak", 1),
            Map.entry("skew", 2),
            Map.entry("kurt", 3),
            Map.entry("rms", 4),
            Map.entry("crestFactor", 5),
            Map.entry("clearanceFactor", 6),
            Map.entry("impulseFactor", 7),
            Map.entry("shapeFactor", 8),
            Map.entry("meanFreq", 9),
            Map.entry("freqCenter", 10),
            Map.entry("rmsFreq", 11),
            Map.entry("sdFreq", 12)
    );

    /**
     * 查询原始特征值
     *
     * @param selectDto
     * @return
     */
    @Override
    public FeatureVo queryFeature(SelectDto selectDto) {
        List<RawDataDto> list = queryRawData(selectDto);
        list = list.stream().filter(l -> l.getAccFeatureX() != null).toList();
        int size = list.size();
        boolean isAcc = "acc".equals(selectDto.getPhysicalType());
        String[] eventTime = new String[size];
        Double[] x = new Double[size];
        Double[] y = new Double[size];
        Double[] z = new Double[size];
        for (int i = 0; i < size; i++) {
            RawDataDto rawDataDto = list.get(i);
            eventTime[i] = DateUtils.formatDate(rawDataDto.getTimestamp());
            int index = featureIndexMap.getOrDefault(selectDto.getFeatureType(), -1);
            if (index == -1) {
//                throw new IllegalArgumentException("Invalid feature type: " + selectDto.getFeatureType());
                continue;
            }
            x[i] = isAcc ? rawDataDto.getAccFeatureX()[index] : rawDataDto.getVelFeatureX()[index];
            y[i] = isAcc ? rawDataDto.getAccFeatureY()[index] : rawDataDto.getVelFeatureY()[index];
            z[i] = isAcc ? rawDataDto.getAccFeatureZ()[index] : rawDataDto.getVelFeatureZ()[index];
        }
        FeatureVo featureVo = new FeatureVo();
        featureVo.setEventTime(eventTime);
        featureVo.setX(x);
        featureVo.setY(y);
        featureVo.setZ(z);
        return featureVo;
    }

    /**
     * 读取文件
     *
     * @param filePath
     * @return
     */
    public String readFile(String filePath)
    {
        if (filePath == null) return null;
        try {
            return new String(Files.readAllBytes(Paths.get(filePath)));
        } catch (Exception e) {
            log.error("json file read failed {}", filePath);
            return null;
        }
    }

    /**
     * 删除文件
     *
     * @param filePath
     * @return
     */
    public boolean deleteFile(String filePath)
    {
        if (filePath == null) return false;
        try {
            return Files.deleteIfExists(Paths.get(filePath));
        } catch (Exception e) {
            return false;
        }
    }
}
